Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Displaying Raw • Download


Examples/Buffer.py 72db6e0ef2e9da96b5f164eb7c37ddbf3022bdec (72db6e0e) Text, 10.18 KB

T8b949e##########################################################
T8b949e# This RNS example demonstrates how to set up a link to #
T8b949e# a destination, and pass binary data over it using a #
T8b949e# channel buffer. #
T8b949e##########################################################
Tff7b72from T7ee787__future__ Tff7b72import Te6edf3annotations
Tff7b72import T7ee787os
Tff7b72import T7ee787sys
Tff7b72import T7ee787time
Tff7b72import T7ee787argparse
Tff7b72from T7ee787datetime Tff7b72import Te6edf3datetime

Tff7b72import T7ee787RNS
Tff7b72from T7ee787RNST7ee787.T7ee787vendor Tff7b72import Te6edf3umsgpack

T8b949e# Let's define an app name. We'll use this for all
T8b949e# destinations we create. Since this echo example
T8b949e# is part of a range of example utilities, we'll put
T8b949e# them all within the app namespace "example_utilities"
Te6edf3APP_NAME Tff7b72= Ta5d6ff"Ta5d6ffexample_utilitiesTa5d6ff"


T8b949e##########################################################
T8b949e#### Server Part #########################################
T8b949e##########################################################

T8b949e# A reference to the latest client link that connected
Te6edf3latest_client_link Tff7b72= Tff7b72None

T8b949e# A reference to the latest buffer object
Te6edf3latest_buffer Tff7b72= Tff7b72None

T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a server
Tff7b72def Td2a8ffserverTb4b4b4(Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Randomly create a new identity for our example
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTb4b4b4(Tb4b4b4)

T8b949e# We create a destination that clients can connect to. We
T8b949e# want clients to create links to this destination, so we
T8b949e# need to create a "single" destination type.
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffINTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffbufferexampleTa5d6ff"
Tb4b4b4)

T8b949e# We configure a function that will get called every time
T8b949e# a new client creates a link to this destination.
Te6edf3server_destinationTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3client_connectedTb4b4b4)

T8b949e# Everything's ready!
T8b949e# Let's Wait for client requests or user input
Te6edf3server_loopTb4b4b4(Te6edf3server_destinationTb4b4b4)

Tff7b72def Td2a8ffserver_loopTb4b4b4(Te6edf3destinationTb4b4b4)Tb4b4b4:
T8b949e# Let the user know that everything is ready
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffLink buffer example Ta5d6ff"Tff7b72+
Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tff7b72+
Ta5d6ff"Ta5d6ff running, waiting for a connection.Ta5d6ff"
Tb4b4b4)

Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffHit enter to manually send an announce (Ctrl-C to quit)Ta5d6ff"Tb4b4b4)

T8b949e# We enter a loop that runs until the users exits.
T8b949e# If the user hits enter, we will announce our server
T8b949e# destination on the network, which will let clients
T8b949e# know how to create messages directed towards it.
Tff7b72while Tff7b72TrueTb4b4b4:
Te6edf3entered Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
Te6edf3destinationTff7b72.Td2a8ffannounceTb4b4b4(Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffSent announce from Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tb4b4b4)

T8b949e# When a client establishes a link to our server
T8b949e# destination, this function will be called with
T8b949e# a reference to the link.
Tff7b72def Td2a8ffclient_connectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3latest_client_linkTb4b4b4, Te6edf3latest_buffer
Te6edf3latest_client_link Tff7b72= Te6edf3link

Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient connectedTa5d6ff"Tb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3client_disconnectedTb4b4b4)

T8b949e# If a new connection is received, the old reader
T8b949e# needs to be disconnected.
Tff7b72if Te6edf3latest_bufferTb4b4b4:
Te6edf3latest_bufferTff7b72.Td2a8ffcloseTb4b4b4(Tb4b4b4)


T8b949e# Create buffer objects.
T8b949e# The stream_id parameter to these functions is
T8b949e# a bit like a file descriptor, except that it
T8b949e# is unique to the *receiver*.
T8b949e#
T8b949e# In this example, both the reader and the writer
T8b949e# use stream_id = 0, but there are actually two
T8b949e# separate unidirectional streams flowing in
T8b949e# opposite directions.
T8b949e#
Te6edf3channel Tff7b72= Te6edf3linkTff7b72.Td2a8ffget_channelTb4b4b4(Tb4b4b4)
Te6edf3latest_buffer Tff7b72= Te6edf3RNSTff7b72.Td2a8ffBufferTff7b72.Td2a8ffcreate_bidirectional_bufferTb4b4b4(T79c0ff0Tb4b4b4, T79c0ff0Tb4b4b4, Te6edf3channelTb4b4b4, Te6edf3server_buffer_readyTb4b4b4)

Tff7b72def Td2a8ffclient_disconnectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient disconnectedTa5d6ff"Tb4b4b4)

Tff7b72def Td2a8ffserver_buffer_readyTb4b4b4(Te6edf3ready_bytesTb4b4b4: Tffa657intTb4b4b4)Tb4b4b4:
T8b949e"""
Callback from buffer when buffer has data available

:param ready_bytes: The number of bytes ready to read
"""
Tff7b72global Te6edf3latest_buffer

Te6edf3data Tff7b72= Te6edf3latest_bufferTff7b72.Td2a8ffreadTb4b4b4(Te6edf3ready_bytesTb4b4b4)
Te6edf3data Tff7b72= Te6edf3dataTff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)

Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffReceived data over the buffer: Ta5d6ff" Tff7b72+ Te6edf3dataTb4b4b4)

Te6edf3reply_message Tff7b72= Ta5d6ff"Ta5d6ffI received Tffea00\"Ta5d6ff"Tff7b72+Te6edf3dataTff7b72+Ta5d6ff"Tffea00\"Ta5d6ff over the bufferTa5d6ff"
Te6edf3reply_message Tff7b72= Te6edf3reply_messageTff7b72.Td2a8ffencodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Te6edf3latest_bufferTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3reply_messageTb4b4b4)
Te6edf3latest_bufferTff7b72.Td2a8ffflushTb4b4b4(Tb4b4b4)




T8b949e##########################################################
T8b949e#### Client Part #########################################
T8b949e##########################################################

T8b949e# A reference to the server link
Te6edf3server_link Tff7b72= Tff7b72None

T8b949e# A reference to the buffer object, needed to share the
T8b949e# object from the link connected callback to the client
T8b949e# loop.
Te6edf3buffer Tff7b72= Tff7b72None

T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a client
Tff7b72def Td2a8ffclientTb4b4b4(Te6edf3destination_hexhashTb4b4b4, Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We need a binary representation of the destination
T8b949e# hash that was entered on the command line
Tff7b72tryTb4b4b4:
Te6edf3dest_len Tff7b72= Tb4b4b4(Te6edf3RNSTff7b72.Td2a8ffReticulumTff7b72.Td2a8ffTRUNCATED_HASHLENGTHTff7b72/Tff7b72/T79c0ff8Tb4b4b4)Tff7b72*T79c0ff2
Tff7b72if Tffa657lenTb4b4b4(Te6edf3destination_hexhashTb4b4b4) Tff7b72!= Te6edf3dest_lenTb4b4b4:
Tff7b72raise Tf85149ValueErrorTb4b4b4(
Ta5d6ff"Ta5d6ffDestination length is invalid, must be Tffd700{hex}Ta5d6ff hexadecimal characters (Tffd700{byte}Ta5d6ff bytes).Ta5d6ff"Tff7b72.Td2a8ffformatTb4b4b4(Tffa657hexTff7b72=Te6edf3dest_lenTb4b4b4, Te6edf3byteTff7b72=Te6edf3dest_lenTff7b72/Tff7b72/T79c0ff2Tb4b4b4)
Tb4b4b4)
Te6edf3destination_hash Tff7b72= Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Te6edf3destination_hexhashTb4b4b4)
Tff7b72exceptTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffInvalid destination entered. Check your input!Tffea00\nTa5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)

T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)

T8b949e# Check if we know a path to the destination
Tff7b72if Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffDestination is not yet known. Requesting path and waiting for announce to arrive...Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffrequest_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)
Tff7b72while Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)

T8b949e# Recall the server identity
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTff7b72.Td2a8ffrecallTb4b4b4(Te6edf3destination_hashTb4b4b4)

T8b949e# Inform the user that we'll begin connecting
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffEstablishing link with server...Ta5d6ff"Tb4b4b4)

T8b949e# When the server identity is known, we set
T8b949e# up a destination
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffOUTTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffbufferexampleTa5d6ff"
Tb4b4b4)

T8b949e# And create a link
Te6edf3link Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTb4b4b4(Te6edf3server_destinationTb4b4b4)

T8b949e# We'll also set up functions to inform the
T8b949e# user when the link is established or closed
Te6edf3linkTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3link_establishedTb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3link_closedTb4b4b4)

T8b949e# Everything is set up, so let's enter a loop
T8b949e# for the user to interact with the example
Te6edf3client_loopTb4b4b4(Tb4b4b4)

Tff7b72def Td2a8ffclient_loopTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3server_link

T8b949e# Wait for the link to become active
Tff7b72while Tff7b72not Te6edf3server_linkTb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)

Te6edf3should_quit Tff7b72= Tff7b72False
Tff7b72while Tff7b72not Te6edf3should_quitTb4b4b4:
Tff7b72tryTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff> Ta5d6ff"Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4)
Te6edf3text Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)

T8b949e# Check if we should quit the example
Tff7b72if Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffquitTa5d6ff" Tff7b72or Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffqTa5d6ff" Tff7b72or Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffexitTa5d6ff"Tb4b4b4:
Te6edf3should_quit Tff7b72= Tff7b72True
Te6edf3server_linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
Tff7b72elseTb4b4b4:
T8b949e# Otherwise, encode the text and write it to the buffer.
Te6edf3text Tff7b72= Te6edf3textTff7b72.Td2a8ffencodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Te6edf3bufferTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3textTb4b4b4)
T8b949e# Flush the buffer to force the data to be sent.
Te6edf3bufferTff7b72.Td2a8ffflushTb4b4b4(Tb4b4b4)


Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffError while sending data over the link buffer: Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4)
Te6edf3should_quit Tff7b72= Tff7b72True
Te6edf3server_linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)

T8b949e# This function is called when a link
T8b949e# has been established with the server
Tff7b72def Td2a8fflink_establishedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
T8b949e# We store a reference to the link
T8b949e# instance for later use
Tff7b72global Te6edf3server_linkTb4b4b4, Te6edf3buffer
Te6edf3server_link Tff7b72= Te6edf3link

T8b949e# Create buffer, see server_client_connected() for
T8b949e# more detail about setting up the buffer.
Te6edf3channel Tff7b72= Te6edf3linkTff7b72.Td2a8ffget_channelTb4b4b4(Tb4b4b4)
Te6edf3buffer Tff7b72= Te6edf3RNSTff7b72.Td2a8ffBufferTff7b72.Td2a8ffcreate_bidirectional_bufferTb4b4b4(T79c0ff0Tb4b4b4, T79c0ff0Tb4b4b4, Te6edf3channelTb4b4b4, Te6edf3client_buffer_readyTb4b4b4)

T8b949e# Inform the user that the server is
T8b949e# connected
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink established with server, enter some text to send, or Tffea00\"Ta5d6ffquitTffea00\"Ta5d6ff to quitTa5d6ff"Tb4b4b4)

T8b949e# When a link is closed, we'll inform the
T8b949e# user, and exit the program
Tff7b72def Td2a8fflink_closedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffTIMEOUTTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link timed out, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elif Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffDESTINATION_CLOSEDTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link was closed by the server, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink closed, exiting nowTa5d6ff"Tb4b4b4)
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff1.5Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)

T8b949e# When the buffer has new data, read it and write it to the terminal.
Tff7b72def Td2a8ffclient_buffer_readyTb4b4b4(Te6edf3ready_bytesTb4b4b4: Tffa657intTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3buffer
Te6edf3data Tff7b72= Te6edf3bufferTff7b72.Td2a8ffreadTb4b4b4(Te6edf3ready_bytesTb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffReceived data over the link buffer: Ta5d6ff" Tff7b72+ Te6edf3dataTff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff> Ta5d6ff"Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffstdoutTff7b72.Td2a8ffflushTb4b4b4(Tb4b4b4)


T8b949e##########################################################
T8b949e#### Program Startup #####################################
T8b949e##########################################################

T8b949e# This part of the program runs at startup,
T8b949e# and parses input of from the user, and then
T8b949e# starts up the desired program mode.
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3parser Tff7b72= Te6edf3argparseTff7b72.Td2a8ffArgumentParserTb4b4b4(Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffSimple buffer exampleTa5d6ff"Tb4b4b4)

Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff-sTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--serverTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstore_trueTa5d6ff"Tb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffwait for incoming link requests from clientsTa5d6ff"
Tb4b4b4)

Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff--configTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffpath to alternative Reticulum config directoryTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)

Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ffdestinationTa5d6ff"Tb4b4b4,
Te6edf3nargsTff7b72=Ta5d6ff"Ta5d6ff?Ta5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffhexadecimal hash of the server destinationTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)

Te6edf3args Tff7b72= Te6edf3parserTff7b72.Td2a8ffparse_argsTb4b4b4(Tb4b4b4)

Tff7b72if Te6edf3argsTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3configarg Tff7b72= Te6edf3argsTff7b72.Td2a8ffconfig
Tff7b72elseTb4b4b4:
Te6edf3configarg Tff7b72= Tff7b72None

Tff7b72if Te6edf3argsTff7b72.Td2a8ffserverTb4b4b4:
Te6edf3serverTb4b4b4(Te6edf3configargTb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72if Tb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestination Tff7b72== Tff7b72NoneTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffprint_helpTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3clientTb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestinationTb4b4b4, Te6edf3configargTb4b4b4)

Tff7b72except Tf85149KeyboardInterruptTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────